#pragma once

#include <memory>
#include <utility>

#include <common/global_data.h>
#include <croutine/croutine.h>
#include <data/data_visitor.h>

class RoutineFactory{
    public:
        using VoidFunc = std::function<void()>;
        using CreateRoutineFunc = std::function<VoidFunc()>;

        CreateRoutineFunc create_routine;

        inline std::shared_ptr<DataVisitorBase> GetDataVisitor() const{
            return data_visitor_;
        }
        inline void SetDataVisitor(const std::shared_ptr<DataVisitorBase>& dv){
            data_visitor_ = dv;
        }
    private:
        std::shared_ptr<DataVisitorBase> data_visitor_ = nullptr;
};


template <typename M0, typename F>
RoutineFactory CreateRoutineFactory(
        F&& f, const std::shared_ptr<DataVisitor<M0>>& dv){

    RoutineFactory factory;
    factory.SetDataVisitor(dv);
    factory.create_routine = [=]() {
        return [=]() { 
            std::shared_ptr<M0> msg;
            for(;;){
                CRoutine::GetCurrentRoutine()->set_state(RoutineState::DATA_WAIT);
                if(dv->TryFetch(msg)){
                    f(msg);  
                    CRoutine::Yield(RoutineState::READY);
                }else{
                    CRoutine::Yield();
                }
            }
        };
    };
    return factory;
}
